package plus.easydo.starter.lock.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import plus.easydo.core.exception.LockFailedException;
import plus.easydo.starter.lock.annotation.RedissonRedLock;

import java.lang.reflect.Method;

/**
 * @author yuzhanfeng
 */
@Aspect
public class RedissonRedLockAspect {

    Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    RedissonClient redissonClient;

    @Around("@annotation(plus.easydo.starter.lock.annotation.RedissonRedLock)")
    public Object redissonRedLock(ProceedingJoinPoint point) throws Throwable {
        Signature signature = point.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        RedissonRedLock annotation = method.getAnnotation(RedissonRedLock.class);
        if (annotation == null) {
            return point.proceed();
        }
        String[] key = annotation.key();
        if (key.length > 0) {
            org.redisson.RedissonRedLock redLock = getRedissonRedLock(key);
            try {
                boolean result = redLock.tryLock(annotation.waitTime(), annotation.leaseTime(), annotation.timeUnit());
                if(!result) {
                    logger.error("线程：{} 获取锁失败",Thread.currentThread().getName());
                    throw new LockFailedException(annotation.lockFailedMessage());
                }
                return point.proceed();
            }finally {
                redLock.unlock();
            }
        }else {
            throw new LockFailedException("the Key Of RedissonRedLock Cannot Be Empty");
        }
    }

    private org.redisson.RedissonRedLock getRedissonRedLock(String[] key) {
        RLock[] locks = new RLock[key.length];
        for (int i = 0; i < locks.length; i++) {
            locks[i] = redissonClient.getLock(key[i]);
        }
        return new org.redisson.RedissonRedLock(locks);
    }
}
